home *** CD-ROM | disk | FTP | other *** search
/ Programming Windows 95 / Programming Windows 95.iso / code / CHAP17 / DDEPOP1.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-01  |  19.7 KB  |  597 lines

  1. /*---------------------------------------------
  2.    DDEPOP1.C -- DDE Server for Population Data
  3.                 (c) Charles Petzold, 1996
  4.   ---------------------------------------------*/
  5.  
  6. #include <windows.h>
  7. #include <dde.h>
  8. #include <string.h>
  9. #include "ddepop.h"
  10.  
  11. typedef struct
  12.      {
  13.      unsigned int fAdvise:1 ;
  14.      unsigned int fDeferUpd:1 ;
  15.      unsigned int fAckReq:1 ;
  16.      unsigned int dummy:13 ;
  17.      long         lPopPrev ;
  18.      }
  19.      POPADVISE ;
  20.  
  21. #define ID_TIMER    1
  22. #define DDE_TIMEOUT 3000
  23.  
  24. LRESULT CALLBACK WndProc         (HWND, UINT, WPARAM, LPARAM) ;
  25. LRESULT CALLBACK ServerProc      (HWND, UINT, WPARAM, LPARAM) ;
  26. BOOL    CALLBACK TimerEnumProc   (HWND, LONG) ;
  27. BOOL    CALLBACK CloseEnumProc   (HWND, LONG) ;
  28. BOOL             PostDataMessage (HWND, HWND, int, BOOL, BOOL, BOOL) ;
  29.  
  30. char      szAppName[]     = "DdePop1" ;
  31. char      szServerClass[] = "DdePop1.Server" ;
  32. HINSTANCE hInst ;
  33.  
  34. int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
  35.                     PSTR szCmdLine, int iCmdShow)
  36.      {
  37.      HWND        hwnd ;
  38.      MSG         msg ;
  39.      WNDCLASSEX  wndclass ;
  40.  
  41.      hInst = hInstance ;
  42.  
  43.                // Register window class
  44.  
  45.      wndclass.cbSize        = sizeof (wndclass) ;
  46.      wndclass.style         = 0 ;
  47.      wndclass.lpfnWndProc   = WndProc ;
  48.      wndclass.cbClsExtra    = 0 ;
  49.      wndclass.cbWndExtra    = 0 ;
  50.      wndclass.hInstance     = hInstance ;
  51.      wndclass.hIcon         = LoadIcon (hInstance, szAppName) ;
  52.      wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
  53.      wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
  54.      wndclass.lpszMenuName  = NULL ;
  55.      wndclass.lpszClassName = szAppName ;
  56.      wndclass.hIconSm       = LoadIcon (hInstance, szAppName) ;
  57.  
  58.      RegisterClassEx (&wndclass) ;
  59.  
  60.                // Register window class for DDE Server
  61.  
  62.      wndclass.cbSize        = sizeof (wndclass) ;
  63.      wndclass.style         = 0 ;
  64.      wndclass.lpfnWndProc   = ServerProc ;
  65.      wndclass.cbClsExtra    = 0 ;
  66.      wndclass.cbWndExtra    = 2 * sizeof (DWORD) ;
  67.      wndclass.hInstance     = hInstance ;
  68.      wndclass.hIcon         = NULL ;
  69.      wndclass.hCursor       = NULL ;
  70.      wndclass.hbrBackground = NULL ;
  71.      wndclass.lpszMenuName  = NULL ;
  72.      wndclass.lpszClassName = szServerClass ;
  73.      wndclass.hIconSm       = NULL ;
  74.  
  75.      RegisterClassEx (&wndclass) ;
  76.  
  77.      hwnd = CreateWindow (szAppName, "DDE Population Server",
  78.                           WS_OVERLAPPEDWINDOW,
  79.                           CW_USEDEFAULT, CW_USEDEFAULT,
  80.                           CW_USEDEFAULT, CW_USEDEFAULT,
  81.                           NULL, NULL, hInstance, NULL) ;
  82.  
  83.      InitPops () ;                      // initialize 'pop' structure
  84.  
  85.      SetTimer (hwnd, ID_TIMER, 5000, NULL) ;
  86.  
  87.      ShowWindow (hwnd, SW_SHOWMINNOACTIVE) ;
  88.      UpdateWindow (hwnd) ;
  89.  
  90.      while (GetMessage (&msg, NULL, 0, 0))
  91.           {
  92.           TranslateMessage (&msg) ;
  93.           DispatchMessage (&msg) ;
  94.           }
  95.  
  96.      KillTimer (hwnd, ID_TIMER) ;
  97.  
  98.      return msg.wParam ;
  99.      }
  100.  
  101. LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
  102.      {
  103.      static char szTopic[] = "US_Population" ;
  104.      ATOM        aApp, aTop ;
  105.      HWND        hwndClient, hwndServer ;
  106.  
  107.      switch (iMsg)
  108.           {
  109.           case WM_DDE_INITIATE :
  110.  
  111.                     // wParam          -- sending window handle
  112.                     // LOWORD (lParam) -- application atom
  113.                     // HIWORD (lParam) -- topic atom
  114.  
  115.                hwndClient = (HWND) wParam ;
  116.  
  117.                aApp = GlobalAddAtom (szAppName) ;
  118.                aTop = GlobalAddAtom (szTopic) ;
  119.  
  120.                     // Check for matching atoms, create window, and acknowledge
  121.  
  122.                if ((LOWORD (lParam) == NULL || LOWORD (lParam) == aApp) &&
  123.                    (HIWORD (lParam) == NULL || HIWORD (lParam) == aTop))
  124.                     {
  125.                     hwndServer = CreateWindow (szServerClass, NULL,
  126.                                                WS_CHILD, 0, 0, 0, 0,
  127.                                                hwnd, NULL, hInst, NULL) ;
  128.  
  129.                     SetWindowLong (hwndServer, 0, (LONG) hwndClient) ;
  130.                     SendMessage ((HWND) wParam, WM_DDE_ACK,
  131.                                  (WPARAM) hwndServer,
  132.                                  MAKELPARAM (aApp, aTop)) ;
  133.                     }
  134.  
  135.                     // Otherwise, delete the atoms just created
  136.  
  137.                else
  138.                     {
  139.                     GlobalDeleteAtom (aApp) ;
  140.                     GlobalDeleteAtom (aTop) ;
  141.                     }
  142.  
  143.                return 0 ;
  144.  
  145.           case WM_TIMER :
  146.           case WM_TIMECHANGE :
  147.  
  148.                     // Calculate new current populations
  149.  
  150.                CalcPops () ;
  151.  
  152.                     // Notify all child windows
  153.  
  154.                EnumChildWindows (hwnd, &TimerEnumProc, 0L) ;
  155.                return 0 ;
  156.  
  157.           case WM_QUERYOPEN :
  158.                return 0 ;
  159.  
  160.           case WM_CLOSE :
  161.  
  162.                     // Notify all child windows
  163.  
  164.                EnumChildWindows (hwnd, &CloseEnumProc, 0L) ;
  165.  
  166.                break ;                  // for default processing
  167.  
  168.           case WM_DESTROY :
  169.                PostQuitMessage (0) ;
  170.                return 0 ;
  171.           }
  172.      return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
  173.      }
  174.  
  175. LRESULT CALLBACK ServerProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
  176.      {
  177.      ATOM          aItem ;
  178.      char          szItem[10] ;
  179.      DDEACK        DdeAck ;
  180.      DDEADVISE    *pDdeAdvise ;
  181.      DWORD         dwTime ;
  182.      GLOBALHANDLE  hPopAdvise, hDdeAdvise, hCommands, hDdePoke ;
  183.      int           i ;
  184.      UINT          uiLow, uiHi ;
  185.      HWND          hwndClient ;
  186.      MSG           msg ;
  187.      POPADVISE    *pPopAdvise ;
  188.      WORD          cfFormat, wStatus ;
  189.  
  190.      switch (iMsg)
  191.           {
  192.           case WM_CREATE :
  193.  
  194.                     // Allocate memory for POPADVISE structures
  195.  
  196.                hPopAdvise = GlobalAlloc (GHND, NUM_STATES * sizeof (POPADVISE));
  197.  
  198.                if (hPopAdvise == NULL)
  199.                     DestroyWindow (hwnd) ;
  200.                else
  201.                     SetWindowLong (hwnd, 4, (LONG) hPopAdvise) ;
  202.  
  203.                return 0 ;
  204.  
  205.           case WM_DDE_REQUEST :
  206.  
  207.                     // wParam          -- sending window handle
  208.                     // LOWORD (lParam) -- data format
  209.                     // HIWORD (lParam) -- item atom
  210.  
  211.                hwndClient = (HWND) wParam ;
  212.                cfFormat   = LOWORD (lParam) ;
  213.                aItem      = HIWORD (lParam) ;
  214.  
  215.                     // Check for matching format and data item
  216.  
  217.                if (cfFormat == CF_TEXT)
  218.                     {
  219.                     GlobalGetAtomName (aItem, szItem, sizeof (szItem)) ;
  220.  
  221.                     for (i = 0 ; i < NUM_STATES ; i++)
  222.                          if (strcmp (szItem, pop[i].szState) == 0)
  223.                               break ;
  224.  
  225.                     if (i < NUM_STATES)
  226.                          {
  227.                          GlobalDeleteAtom (aItem) ;
  228.                          PostDataMessage (hwnd, hwndClient, i,
  229.                                           FALSE, FALSE, TRUE) ;
  230.                          return 0 ;
  231.                          }
  232.                     }
  233.  
  234.                     // Negative acknowledge if no match
  235.  
  236.                DdeAck.bAppReturnCode = 0 ;
  237.                DdeAck.reserved       = 0 ;
  238.                DdeAck.fBusy          = FALSE ;
  239.                DdeAck.fAck           = FALSE ;
  240.  
  241.                wStatus = *((WORD *) &DdeAck) ;
  242.  
  243.                if (!PostMessage (hwndClient, WM_DDE_ACK, (WPARAM) hwnd,
  244.                                  PackDDElParam (WM_DDE_ACK, wStatus, aItem)))
  245.                     {
  246.                     GlobalDeleteAtom (aItem) ;
  247.                     }
  248.  
  249.                return 0 ;
  250.  
  251.           case WM_DDE_ADVISE :
  252.  
  253.                     // wParam -- sending window handle
  254.                     // lParam -- DDEADVISE memory handle & item atom
  255.  
  256.                UnpackDDElParam (WM_DDE_ADVISE, lParam, &uiLow, &uiHi) ;
  257.                FreeDDElParam (WM_DDE_ADVISE, lParam) ;
  258.  
  259.                hwndClient = (HWND) wParam ;
  260.                hDdeAdvise = (GLOBALHANDLE) uiLow ;
  261.                aItem      = (ATOM) uiHi ;
  262.  
  263.                pDdeAdvise = (DDEADVISE *) GlobalLock (hDdeAdvise) ;
  264.  
  265.                     // Check for matching format and data item
  266.  
  267.                if (pDdeAdvise->cfFormat == CF_TEXT)
  268.                     {
  269.                     GlobalGetAtomName (aItem, szItem, sizeof (szItem)) ;
  270.  
  271.                     for (i = 0 ; i < NUM_STATES ; i++)
  272.                          if (strcmp (szItem, pop[i].szState) == 0)
  273.                               break ;
  274.  
  275.                          // Fill in the POPADVISE structure and acknowledge
  276.  
  277.                     if (i < NUM_STATES)
  278.                          {
  279.                          hPopAdvise = (GLOBALHANDLE) GetWindowLong (hwnd, 4) ;
  280.                          pPopAdvise = (POPADVISE *)
  281.                                               GlobalLock (hPopAdvise) ;
  282.  
  283.                          pPopAdvise[i].fAdvise   = TRUE ;
  284.                          pPopAdvise[i].fDeferUpd = pDdeAdvise->fDeferUpd ;
  285.                          pPopAdvise[i].fAckReq   = pDdeAdvise->fAckReq ;
  286.                          pPopAdvise[i].lPopPrev  = pop[i].lPop ;
  287.  
  288.                          GlobalUnlock (hDdeAdvise) ;
  289.                          GlobalFree (hDdeAdvise) ;
  290.  
  291.                          DdeAck.bAppReturnCode = 0 ;
  292.                          DdeAck.reserved       = 0 ;
  293.                          DdeAck.fBusy          = FALSE ;
  294.                          DdeAck.fAck           = TRUE ;
  295.  
  296.                          wStatus = *((WORD *) &DdeAck) ;
  297.  
  298.                          if (!PostMessage (hwndClient, WM_DDE_ACK,
  299.                                            (WPARAM) hwnd,
  300.                                            PackDDElParam (WM_DDE_ACK,
  301.                                                           wStatus, aItem)))
  302.                               {
  303.                               GlobalDeleteAtom (aItem) ;
  304.                               }
  305.                          else
  306.                               {
  307.                               PostDataMessage (hwnd, hwndClient, i,
  308.                                                pPopAdvise[i].fDeferUpd,
  309.                                                pPopAdvise[i].fAckReq,
  310.                                                FALSE) ;
  311.                               }
  312.  
  313.                          GlobalUnlock (hPopAdvise) ;
  314.                          return 0 ;
  315.                          }
  316.                     }
  317.  
  318.                          // Otherwise, post a negative WM_DDE_ACK
  319.  
  320.                GlobalUnlock (hDdeAdvise) ;
  321.  
  322.                DdeAck.bAppReturnCode = 0 ;
  323.                DdeAck.reserved       = 0 ;
  324.                DdeAck.fBusy          = FALSE ;
  325.                DdeAck.fAck           = FALSE ;
  326.  
  327.                wStatus = *((WORD *) &DdeAck) ;
  328.  
  329.                if (!PostMessage (hwndClient, WM_DDE_ACK, (WPARAM) hwnd,
  330.                                  PackDDElParam (WM_DDE_ACK, wStatus, aItem)))
  331.                     {
  332.                     GlobalFree (hDdeAdvise) ;
  333.                     GlobalDeleteAtom (aItem) ;
  334.                     }
  335.  
  336.                return 0 ;
  337.  
  338.           case WM_DDE_UNADVISE :
  339.  
  340.                     // wParam          -- sending window handle
  341.                     // LOWORD (lParam) -- data format
  342.                     // HIWORD (lParam) -- item atom
  343.  
  344.                hwndClient = (HWND) wParam ;
  345.                cfFormat   = LOWORD (lParam) ;
  346.                aItem      = HIWORD (lParam) ;
  347.  
  348.                DdeAck.bAppReturnCode = 0 ;
  349.                DdeAck.reserved       = 0 ;
  350.                DdeAck.fBusy          = FALSE ;
  351.                DdeAck.fAck           = TRUE ;
  352.  
  353.                hPopAdvise  = (GLOBALHANDLE) GetWindowLong (hwnd, 4) ;
  354.                pPopAdvise = (POPADVISE *) GlobalLock (hPopAdvise) ;
  355.  
  356.                     // Check for matching format and data item
  357.  
  358.                if (cfFormat == CF_TEXT || cfFormat == NULL)
  359.                     {
  360.                     if (aItem == (ATOM) NULL)
  361.                          for (i = 0 ; i < NUM_STATES ; i++)
  362.                               pPopAdvise[i].fAdvise = FALSE ;
  363.                     else
  364.                          {
  365.                          GlobalGetAtomName (aItem, szItem, sizeof (szItem)) ;
  366.  
  367.                          for (i = 0 ; i < NUM_STATES ; i++)
  368.                               if (strcmp (szItem, pop[i].szState) == 0)
  369.                                    break ;
  370.  
  371.                          if (i < NUM_STATES)
  372.                               pPopAdvise[i].fAdvise = FALSE ;
  373.                          else
  374.                               DdeAck.fAck = FALSE ;
  375.                          }
  376.                     }
  377.                else
  378.                     DdeAck.fAck = FALSE ;
  379.  
  380.                     // Acknowledge either positively or negatively
  381.  
  382.                wStatus = *((WORD *) &DdeAck) ;
  383.  
  384.                if (!PostMessage (hwndClient, WM_DDE_ACK, (WPARAM) hwnd,
  385.                                  PackDDElParam (WM_DDE_ACK, wStatus, aItem)))
  386.                     {
  387.                     if (aItem != (ATOM) NULL)
  388.                          GlobalDeleteAtom (aItem) ;
  389.                     }
  390.  
  391.                GlobalUnlock (hPopAdvise) ;
  392.                return 0 ;
  393.  
  394.           case WM_DDE_EXECUTE :
  395.  
  396.                     // Post negative acknowledge
  397.  
  398.                hwndClient = (HWND) wParam ;
  399.                hCommands  = (GLOBALHANDLE) lParam ;
  400.  
  401.                DdeAck.bAppReturnCode = 0 ;
  402.                DdeAck.reserved       = 0 ;
  403.                DdeAck.fBusy          = FALSE ;
  404.                DdeAck.fAck           = FALSE ;
  405.  
  406.                wStatus = *((WORD *) &DdeAck) ;
  407.  
  408.                if (!PostMessage (hwndClient, WM_DDE_ACK, (WPARAM) hwnd,
  409.                                  PackDDElParam (WM_DDE_ACK,
  410.                                                 wStatus, (UINT) hCommands)))
  411.                     {
  412.                     GlobalFree (hCommands) ;
  413.                     }
  414.                return 0 ;
  415.  
  416.           case WM_DDE_POKE :
  417.  
  418.                     // Post negative acknowledge
  419.  
  420.                UnpackDDElParam (WM_DDE_POKE, lParam, &uiLow, &uiHi) ;
  421.                FreeDDElParam (WM_DDE_POKE, lParam) ;
  422.                hwndClient = (HWND) wParam ;
  423.                hDdePoke   = (GLOBALHANDLE) uiLow ;
  424.                aItem      = (ATOM) uiHi ;
  425.  
  426.                DdeAck.bAppReturnCode = 0 ;
  427.                DdeAck.reserved       = 0 ;
  428.                DdeAck.fBusy          = FALSE ;
  429.                DdeAck.fAck           = FALSE ;
  430.  
  431.                wStatus = *((WORD *) &DdeAck) ;
  432.  
  433.                if (!PostMessage (hwndClient, WM_DDE_ACK, (WPARAM) hwnd,
  434.                                  PackDDElParam (WM_DDE_ACK, wStatus, aItem)))
  435.                     {
  436.                     GlobalFree (hDdePoke) ;
  437.                     GlobalDeleteAtom (aItem) ;
  438.                     }
  439.  
  440.                return 0 ;
  441.  
  442.           case WM_DDE_TERMINATE :
  443.  
  444.                     // Respond with another WM_DDE_TERMINATE iMsg
  445.  
  446.                hwndClient = (HWND) wParam ;
  447.                PostMessage (hwndClient, WM_DDE_TERMINATE, (WPARAM) hwnd, 0L) ;
  448.                DestroyWindow (hwnd) ;
  449.                return 0 ;
  450.  
  451.           case WM_TIMER :
  452.  
  453.                     // Post WM_DDE_DATA iMsgs for changed populations
  454.  
  455.                hwndClient = (HWND) GetWindowLong (hwnd, 0) ;
  456.                hPopAdvise = (GLOBALHANDLE) GetWindowLong (hwnd, 4) ;
  457.                pPopAdvise = (POPADVISE *) GlobalLock (hPopAdvise) ;
  458.  
  459.                for (i = 0 ; i < NUM_STATES ; i++)
  460.                     if (pPopAdvise[i].fAdvise)
  461.                          if (pPopAdvise[i].lPopPrev != pop[i].lPop)
  462.                               {
  463.                               if (!PostDataMessage (hwnd, hwndClient, i,
  464.                                                     pPopAdvise[i].fDeferUpd,
  465.                                                     pPopAdvise[i].fAckReq,
  466.                                                     FALSE))
  467.                                    break ;
  468.  
  469.                               pPopAdvise[i].lPopPrev = pop[i].lPop ;
  470.                               }
  471.  
  472.                GlobalUnlock (hPopAdvise) ;
  473.                return 0 ;
  474.  
  475.           case WM_CLOSE :
  476.  
  477.                     // Post a WM_DDE_TERMINATE iMsg to the client
  478.  
  479.                hwndClient = (HWND) GetWindowLong (hwnd, 0) ;
  480.                PostMessage (hwndClient, WM_DDE_TERMINATE, (WPARAM) hwnd, 0L) ;
  481.  
  482.                dwTime = GetCurrentTime () ;
  483.  
  484.                while (GetCurrentTime () - dwTime < DDE_TIMEOUT)
  485.                     if (PeekMessage (&msg, hwnd, WM_DDE_TERMINATE,
  486.                                      WM_DDE_TERMINATE, PM_REMOVE))
  487.                          break ;
  488.  
  489.                DestroyWindow (hwnd) ;
  490.                return 0 ;
  491.  
  492.           case WM_DESTROY :
  493.                hPopAdvise = (GLOBALHANDLE) GetWindowLong (hwnd, 4) ;
  494.                GlobalFree (hPopAdvise) ;
  495.                return 0 ;
  496.           }
  497.      return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
  498.      }
  499.  
  500. BOOL CALLBACK TimerEnumProc (HWND hwnd, LPARAM lParam)
  501.      {
  502.      SendMessage (hwnd, WM_TIMER, 0, 0L) ;
  503.  
  504.      return TRUE ;
  505.      }
  506.  
  507. BOOL CALLBACK CloseEnumProc (HWND hwnd, LPARAM lParam)
  508.      {
  509.      SendMessage (hwnd, WM_CLOSE, 0, 0L) ;
  510.  
  511.      return TRUE ;
  512.      }
  513.  
  514. BOOL PostDataMessage (HWND hwndServer, HWND hwndClient, int iState,
  515.                       BOOL fDeferUpd, BOOL fAckReq, BOOL fResponse)
  516.      {
  517.      ATOM         aItem ;
  518.      char         szPopulation[16] ;
  519.      DDEACK       DdeAck ;
  520.      DDEDATA     *pDdeData ;
  521.      DWORD        dwTime ;
  522.      GLOBALHANDLE hDdeData ;
  523.      MSG          msg ;
  524.      WORD         wStatus ;
  525.  
  526.      aItem = GlobalAddAtom (pop[iState].szState) ;
  527.  
  528.           // Allocate a DDEDATA structure if not deferred update
  529.  
  530.      if (fDeferUpd)
  531.           {
  532.           hDdeData = NULL ;
  533.           }
  534.      else
  535.           {
  536.           wsprintf (szPopulation, "%ld\r\n", pop[iState].lPop) ;
  537.  
  538.           hDdeData = GlobalAlloc (GHND | GMEM_DDESHARE,
  539.                                   sizeof (DDEDATA) + strlen (szPopulation)) ;
  540.  
  541.           pDdeData = (DDEDATA *) GlobalLock (hDdeData) ;
  542.  
  543.           pDdeData->fResponse = fResponse ;
  544.           pDdeData->fRelease  = TRUE ;
  545.           pDdeData->fAckReq   = fAckReq ;
  546.           pDdeData->cfFormat  = CF_TEXT ;
  547.  
  548.           lstrcpy ((PSTR) pDdeData->Value, szPopulation) ;
  549.  
  550.           GlobalUnlock (hDdeData) ;
  551.           }
  552.  
  553.           // Post the WM_DDE_DATA iMsg
  554.  
  555.      if (!PostMessage (hwndClient, WM_DDE_DATA, (WPARAM) hwndServer,
  556.                        PackDDElParam (WM_DDE_DATA, (UINT) hDdeData, aItem)))
  557.           {
  558.           if (hDdeData != NULL)
  559.                GlobalFree (hDdeData) ;
  560.  
  561.           GlobalDeleteAtom (aItem) ;
  562.           return FALSE ;
  563.           }
  564.  
  565.           // Wait for the acknowledge iMsg if it's requested
  566.  
  567.      if (fAckReq)
  568.           {
  569.           DdeAck.fAck = FALSE ;
  570.  
  571.           dwTime = GetCurrentTime () ;
  572.  
  573.           while (GetCurrentTime () - dwTime < DDE_TIMEOUT)
  574.                {
  575.                if (PeekMessage (&msg, hwndServer, WM_DDE_ACK, WM_DDE_ACK,
  576.                                 PM_REMOVE))
  577.                     {
  578.                     wStatus = LOWORD (msg.lParam) ;
  579.                     DdeAck = *((DDEACK *) &wStatus) ;
  580.                     aItem  = HIWORD (msg.lParam) ;
  581.                     GlobalDeleteAtom (aItem) ;
  582.                     break ;
  583.                     }
  584.                }
  585.  
  586.           if (DdeAck.fAck == FALSE)
  587.                {
  588.                if (hDdeData != NULL)
  589.                     GlobalFree (hDdeData) ;
  590.  
  591.                return FALSE ;
  592.                }
  593.           }
  594.  
  595.      return TRUE ;
  596.      }
  597.